using System;
using System.Collections.Generic;
using System.Text;
using System.ComponentModel;
using System.Collections;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Pavilion;

namespace Pavilion.Control
{
    public class EditDisplay : Control
    {
        private Panel panel;
        private List<object> objectList;
        private Scroller scroller;
        private int minimumHeight, maximumHeight;

        public EditDisplay() : base()
        {
            objectList = new List<object>();
            
            AreaBounds = new Rectangle(0, 0, 240, 250);
            panel = new Panel(AreaBounds);
            maximumHeight = minimumHeight = AreaBounds.Height;

            this._Collection.Add(panel);
        }

        /// <summary>
        /// Adds an object to display.
        /// </summary>
        /// <param name="objectItem">The object that is to be added.</param>
        public void AddObject(object objectItem)
        {
            objectList.Add(objectItem);
            AddPropertiesFromObject(objectItem);
        }

        /// <summary>
        /// Adds a range of objects to be the display.
        /// </summary>
        /// <param name="objectList">The list of objects that are to be displayed.</param>
        public void AddRange(IEnumerable objectList)
        {
            foreach (object objectItem in objectList)
                AddObject(objectItem);
        }

        private void AddPropertiesFromObject(object item)
        {
            PropertyDescriptorCollection propertyList = TypeDescriptor.GetProperties(item);

            foreach (PropertyDescriptor property in propertyList)
            {
                if (property.Category == "Data")
                {
                    if (property.GetValue(item) is IEnumerable)
                        AddPropertiesFromObjectList((IEnumerable)property.GetValue(item));
                    else
                        AddEditItem(CreateEditItem(propertyList, property, item));
                }
                else if (property.Category == "InternalData")
                {
                    AddObject(property.GetValue(item));
                }
            }
        }

        private void AddPropertiesFromObjectList(IEnumerable objectList)
        {
            foreach (object objectItem in objectList)
                AddPropertiesFromObject(objectItem);
        }

        /// <summary>
        /// Adds an Edit Item for a given property of an item.
        /// </summary>
        private void AddEditItem(EditItem editItem)
        {
            ItemCollection.Add(editItem);
            RefreshDisplaySize();
        }

        private EditItem CreateEditItem(PropertyDescriptorCollection properties, PropertyDescriptor property, object item)
        {
            EditItem editItem = new EditItem(new Rectangle(0, TotalItemHeight, panel.AreaBounds.Width, 20), this);
            editItem.Resize += new EventHandler(editItem_Resize);

            if (property.Description != "")
                editItem.SetPropertyDescriptor(property, item, (List<DropDownItem>)properties.Find(property.Description, false).GetValue(item));
            else
                editItem.SetPropertyDescriptor(property, item);

            return editItem;
        }

        /// <summary>
        /// Clears the display of all items.
        /// </summary>
        public void Clear()
        {
            ItemCollection.Clear();
            objectList.Clear();
        }

        /// <summary>
        /// Refreshes the Display.
        /// </summary>
        public void Refresh()
        {
            object[] items = objectList.ToArray();

            Clear();

            for (int i = 0; i < items.Length; i++)
                AddObject(items[i]);

            if (ItemCollection.Count == 0)
                RefreshDisplaySize();
        }

        private void RefreshEditItemPositions()
        {
            int height = 0;

            for (int i = 0; i < ItemCollection.Count; i++)
            {
                ItemCollection[i].Position = new Point(ItemCollection[i].AreaBounds.X, height);
                height += ItemCollection[i].AreaBounds.Height;
            }
        }

        private void RefreshDisplaySize()
        {
            int totalItemHeight = TotalItemHeight;

            panel.AreaBounds = new Rectangle(panel.AreaBounds.X, panel.AreaBounds.Y, panel.AreaBounds.Width, totalItemHeight);

            if (totalItemHeight > MinimumHeight)
            {
                if (totalItemHeight < MaximumHeight)
                    AreaBounds = new Rectangle(AreaBounds.X, AreaBounds.Y, AreaBounds.Width, totalItemHeight);
                else
                    AreaBounds = new Rectangle(AreaBounds.X, AreaBounds.Y, AreaBounds.Width, MaximumHeight);
            }
            else
            {
                AreaBounds = new Rectangle(AreaBounds.X, AreaBounds.Y, AreaBounds.Width, MinimumHeight);
            }

            RefreshScroller();
        }

        private void RefreshScroller()
        {
            if (scroller == null)
                return;

            scroller.MaximumHeight = AreaBounds.Height;
        }

        private void LoadScroller()
        {
            if (scroller != null)
                return;

            scroller = new Scroller();
            panel.AreaBounds = new Rectangle(panel.AreaBounds.X, panel.AreaBounds.Y, panel.AreaBounds.Width - 20, panel.AreaBounds.Height);
            scroller.AttachPanel(panel);
            scroller.MaximumHeight = AreaBounds.Height;
            this._Collection.Add(scroller);
        }

        private void UnloadScroller()
        {
            if (scroller == null)
                return;

            this._Collection.Remove(scroller);
            scroller = null;
            panel.AreaBounds = new Rectangle(panel.AreaBounds.X, AreaBounds.Y, AreaBounds.Width + 20, AreaBounds.Height);
        }

        private Point DetermineViewablePosition(Point desiredPosition)
        {
            if (Parent != null)
            {
                //If it's too far to the right
                if (desiredPosition.X + AreaBounds.Width > Parent.AreaBounds.Width)
                    desiredPosition.X -= AreaBounds.Width;
                //If it's too low to the bottom
                if (desiredPosition.Y + AreaBounds.Height > Parent.AreaBounds.Height)
                    desiredPosition.Y -= AreaBounds.Height;
            }
            return desiredPosition;
        }

        /// <summary>
        /// Gets or sets the boolean value of whether this control has a Scroller.
        /// </summary>
        public bool ContainsScroller
        {
            get
            {
                if (scroller == null)
                    return false;
                else
                    return true;
            }
            set
            {
                if (ContainsScroller == value)
                    return;

                if (value)
                    LoadScroller();
                else
                    UnloadScroller();
            }
        }

        /// <summary>
        /// Gets or sets the Position of this Display.
        /// </summary>
        public override Point Position
        {
            get { return base.Position; }
            set { base.Position = DetermineViewablePosition(value); }
        }

        /// <summary>
        /// Gets or sets the Minimum Height of this Display.
        /// </summary>
        public int MinimumHeight 
        {
            get { return minimumHeight; }
            set
            {
                minimumHeight = value;
                Refresh();
            }
        }

        /// <summary>
        /// Gets or sets the Minimum Height of this Display.
        /// </summary>
        public int MaximumHeight
        {
            get { return maximumHeight; }
            set
            {
                maximumHeight = value;
                Refresh();
            }
        }
        
        /// <summary>
        /// Gets the combined hight of all the items.
        /// </summary>
        public int TotalItemHeight
        {
            get
            {
                int height = 0;

                foreach (Control item in ItemCollection)
                    height += item.AreaBounds.Height;

                return height;
            }
        }

        /// <summary>
        /// Gets Collection of items of this Edit Display.
        /// </summary>
        private NotifiableCollection<Control> ItemCollection
        {
            get { return panel.Collection; }
        }

        private void editItem_Resize(object sender, EventArgs e)
        {
            RefreshEditItemPositions();
            RefreshDisplaySize();
        }
    }
}
